﻿using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using DZPK.SocketImpl;

namespace DZPK
{
    public class Communication
    {
        private Communication()
        { }

        private static Communication comm = null;

        public static Communication GetInstance()
        {
            if (comm == null)
            {
                return new Communication();
            }

            return comm;
        }

        private static object syncObject = new object();

        private static ConcurrentDictionary<Socket, Player> socketToPlayerMap = new ConcurrentDictionary<Socket, Player>();

        private static ArrayList readList = new ArrayList();

        private static bool bNeedStop = false;

        private static int BUFF_SIZE = 8192;

        private static bool bRunning = false;

        private static Task ReceiveServerDataTask = null;

        public static void JoinRead(Player player)
        {
            if (player.receiver.GetSocketInstance() == null)
            {
                throw new Exception("Player has null socket instance.");
            }

            lock (syncObject)
            {
                readList.Add(player.receiver.GetSocketInstance());
                
                if (!bRunning)
                {
                    bRunning = true;

                    ReceiveServerDataTask = new Task(() =>
                    {
                        ProcessCommunication();
                    });

                    ReceiveServerDataTask.Start();
                }
            }

            socketToPlayerMap.GetOrAdd(player.receiver.GetSocketInstance(), player);
        }

        public static void LeaveRead(Player player)
        {
            if (player.receiver.GetSocketInstance() == null)
            {
                throw new Exception("Player has null socket instance.");
            }

            lock (syncObject)
            {
                readList.Remove(player.receiver.GetSocketInstance());
            }

            Player output;
            socketToPlayerMap.TryRemove(player.receiver.GetSocketInstance(), out output);
        }

        private static void TriggerRead(ArrayList readList)
        {
            ArrayList tmpRead = new ArrayList();

            lock (syncObject)
            {
                tmpRead.AddRange(readList);
            }

            if (tmpRead.Count <= 0)
            {
                return;
            }

            int count = tmpRead.Count;
            for (int i = 0; i < count; i++)
            {
                byte[] bytes = new byte[BUFF_SIZE];
                int len;
                Socket socket = tmpRead[i] as Socket;
                if ((len = socket.Receive(bytes)) > 0)
                {
                    Player player = null;

                    if (socketToPlayerMap.TryGetValue(socket, out player))
                    {
                        player.ReceiveServerPacket(bytes, len);
                    }
                }
            }
        }

        public static void ProcessCommunication()
        {
            while (!bNeedStop)
            {
                ArrayList tmpRead = null;

                lock (syncObject)
                {
                    tmpRead = new ArrayList(readList);
                }

                if (tmpRead != null && tmpRead.Count > 0)
                {
                    Socket.Select(tmpRead, null, null, 10);

                    if (tmpRead.Count > 0)
                    {
                        TriggerRead(tmpRead);
                    }
                    else
                    {
                        Thread.Sleep(1000);
                    }
                }
                else 
                {
                    Thread.Sleep(1000);
                }
            }
        }
    }
}
